﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using DbViewDriver;
using System.Data.SQLite;

/*

*/
namespace SqliteDriver
{
	public class SqliteDataAccessor
	{
		public delegate bool ShowProgressDelegate(int count, int done);

		public DataTable Query(String strConnectionString, String[] commandList, String tableName)
		{
			DataSet dset = new DataSet();
			DataTable dt = new DataTable();
			String command = "";
			using (SQLiteConnection con = new SQLiteConnection(strConnectionString))
			{
				try
				{
					con.Open();
					for (int idx = 0; idx < commandList.GetLength(0); ++idx)
					{
						command = commandList[idx];
						if (command.Trim().Length > 2)
						{
							if (IsQuery(command))
							{
								SQLiteDataAdapter da = new SQLiteDataAdapter(command, con);
								// execute into something
								da.Fill(dset, tableName);
								if (dset.Tables.Count > 0)
									dt = dset.Tables[0];
							}
							else
							{
								SQLiteCommand cmd = new SQLiteCommand(command, con);
								int rows = cmd.ExecuteNonQuery();
							}
						}
					}
				}
				catch (SQLiteException sqlexception)
				{
					String message = String.Format("Sql Error: {0}\n" +
												   "connection: {1}\n" +
													"Sql: {2}\n",
													sqlexception.Message, strConnectionString, command);
					Exception ex = new Exception(message);
					throw ex; // abort process having logged as much as possible.
				}
				finally
				{
					con.Close();
				}
			}
			return dt;
		}
		
		public DataTable GenericQuery(String strConnectionString, String sql, String tableName)
		{
			// queries with spaces in are split up
			String[] commandList = new string[] {sql};
			return this.Query(strConnectionString, commandList, tableName);
		}

		// Compatibility
		public String GenericExecute(String strConnectionString, String sql, out bool success)
		{
			String[] commandList = new string[] {sql};
			DataTable dt = this.Query(strConnectionString, commandList, "result");
			success = true;
			return ""; // or some error
		}
		
		public void ResetAutoIdentity(string connStr, String tableName)
		{
		}

		// do you get a data set back or not?
		private bool IsQuery(string sql)
		{
			return (sql.ToUpper().IndexOf("SELECT ") != -1);
		}

		public int Update(String strConnectionString, String referenceSql, DataTable updatedTable, out String statusMessage)
		{
			int status = 0;
			statusMessage = "ok";
			try
			{
				// do it.
				SQLiteDataAdapter myDataAdapter = new SQLiteDataAdapter();
				// the sql and the command builder automatically generate the 
				// insert/update/delete sql
				using (SQLiteConnection connection = new SQLiteConnection(strConnectionString))
				{
					myDataAdapter.SelectCommand = new SQLiteCommand(referenceSql, connection);
					SQLiteCommandBuilder cb = new SQLiteCommandBuilder(myDataAdapter);

					connection.Open();

					// Without the SqlCommandBuilder this line would fail
					// below apparently checks the state of each row in the table
					// and applies the correct sql to update it.
					myDataAdapter.Update(updatedTable);
					connection.Close();
					status = 1;
				}
			}
			catch (DBConcurrencyException e)
			{
				statusMessage = e.Message;
				status = -3;
				//MessageBox.Show(message);
			}
			catch (InvalidOperationException e)
			{
				statusMessage = e.Message;
				status = -2;
			}
			catch (SQLiteException e)
			{
				statusMessage = e.Message;
				status = -1;
			}
			return status;
		}

		public void AccessDatabaseEngineType()
		{
			// it's enough to attempt to construct one.
			using (SQLiteConnection sqlConn = new SQLiteConnection())
			{
			}
		}
/*
	From here down is a duplication of the code in the CE4.0 driver. The duplication is necessary because the difference versions
	of the driver reference different DLLS unless there is a way I don't know about
*/
		// has to be here as it requires access to versioned objects.
//        public void CopyTable(String sourceConnection, String sourceTable, String tgtConnection, String tgtTable, String autoIncField, bool copyContent, ShowProgressDelegate progress)
//        {
//            SQLiteConnection connSource = new  SQLiteConnection();
//            SQLiteConnection connTgt = new  SQLiteConnection();
//            try
//            {
//                // Open the connection and create a SQL command
//                //
//                connSource = new SQLiteConnection(sourceConnection);
//                connSource.Open();

//                SQLiteCommand cmda = new SQLiteCommand("SELECT count(*) FROM " + sourceTable, connSource);
//                SQLiteDataReader  rdra = cmda.ExecuteReader();
//                rdra.Read();
//                int count = (int)rdra[0];

//                SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM " + sourceTable, connSource);
//                SQLiteDataReader  rdr = cmd.ExecuteReader();

//                if (sourceConnection == tgtConnection)
//                    connTgt = connSource;
//                else
//                {
//                    connTgt = new SQLiteConnection(tgtConnection);
//                    connTgt.Open();
//                }
//                // check existance of tgt
//                SQLiteCommand cmd2 = new SQLiteCommand();
//                cmd2.Connection = connTgt;
//                cmd2.CommandText = tgtTable;
//                cmd2.CommandType = CommandType.TableDirect;
//                SQLiteCommand cmd3 = new SQLiteCommand(String.Format("set IDENTITY_INSERT [{0}] ON; ",tgtTable), connTgt);
//                int max = -1;
//                if (autoIncField.Length > 0)
//                {
//                    cmd3.ExecuteNonQuery();
//                    max = 1;
//                }

///*
//Open source / read 1 row at a time / update target

//                SQLiteResultSet rst = cmd2.ExecuteResultSet(ResultSetOptions.Updatable | ResultSetOptions.Scrollable);
//                // initialise progress counters.
//                int done = 0;
//                bool userCancel = false;
//                // Iterate through the results
//                //
//                while (rdr.Read())
//                {
//                    SQLiteUpdatableRecord rec = rst.CreateRecord();
//                    for (int idx = 0; idx < rdr.FieldCount; ++idx)
//                    {
//                        rec[idx] = rdr[idx];
//                    }
//                    rst.Insert(rec);
//                    if (max > -1)
//                    {
//                        int val = (Int32)rec[autoIncField];
//                        if (val > max) max = val;
//                    }
//                    if ((done % 1000) == 0)
//                        userCancel = progress(count, done);
//                    ++done;
//                }
//                // Always dispose data readers and commands as soon as practicable
//                //
//                rst.Close();
//                rdr.Close();
//                cmd.Dispose();
//                cmd2.Dispose();
//                userCancel = progress(count, count);
//B*/

//                if (autoIncField.Length > 0)
//                {
//                    cmd3.CommandText = String.Format("set IDENTITY_INSERT [{0}] OFF; ",tgtTable);
//                    cmd3.ExecuteNonQuery();
//                    cmd3.Dispose();
//                    //cmd3.CommandText = String.Format("alter table [{0}] ALTER COLUMN {1} IDENTITY ({2},1); ",tgtTable, autoIncField, max);
//                    //cmd3.ExecuteNonQuery();
//                    this.ResetAutoIdentity(tgtConnection, tgtTable);
//                }

//            }
//            finally
//            {
//                // Close the connection when no longer needed
//                //
//                connSource.Close();
//                connTgt.Close();
//            }
//        }
		
		public void PopulateTable(String connectionString, String tableName, DataTable data, String autoIncField, bool isStaging)
		{
			SQLiteCommand cmd2 = new SQLiteCommand();
			cmd2.Connection = new SQLiteConnection(connectionString);
            cmd2.Connection.Open();
			// Establish identity field and adjust if necessary
			if (autoIncField.Length > 0)
			{
				// todo 
				//cmd2.CommandText = "set IDENTITY_INSERT "+tableName+" ON";
				//cmd2.ExecuteNonQuery();
			}

			// clone the datatable schema
			try
			{
				DataTable target = data.Clone();
				target.TableName = tableName;
				foreach (DataRow dr in data.Rows)
				{
					target.ImportRow(dr);
				}

				DbView.ViewDefinition viewDef = new DbView.ViewDefinition();
				viewDef.ConfigureFromTable(target);
			
				String sql = "select * from " + target.TableName;
				// cur from old code
				int status = 0;

				int modCount = 0;
				foreach (DataRow dr in target.Rows)
					if (dr.RowState != DataRowState.Unchanged)
						++modCount;
				String statusMessage = String.Format("{0} changed rows", modCount);
				// do it.
				SQLiteDataAdapter myDataAdapter = new SQLiteDataAdapter();
				// the sql and the command builder automatically generate the 
				// insert/update/delete sql
				SQLiteConnection databaseConnection = new SQLiteConnection(connectionString);
				databaseConnection.Open();
				
				// Vital to wrap this in a transaction to get decent performance.
				using (SQLiteTransaction txn = databaseConnection.BeginTransaction())
				{
					myDataAdapter.SelectCommand = new SQLiteCommand(sql, databaseConnection);
					SQLiteCommandBuilder cb = new SQLiteCommandBuilder(myDataAdapter);
					cb.GetInsertCommand().Transaction = txn;
					myDataAdapter.Update(target);
					txn.Commit();
				}
				databaseConnection.Close();
				status = 1;
			}
			catch (Exception)
			{
				// forward all exceptions to caller. Try / Catch is here for resource tidying in finally
				throw;
			}
			finally
			{
				cmd2.Connection.Close();
	 			cmd2.Dispose();
			}

			// fix up the next autoincrement value.
			if (autoIncField.Length > 0)
				this.ResetAutoIdentity(connectionString, tableName);
		}


//        // routine to update a target table from a staging table
//        // just the primary key matching fields.
//        public void UpdateTable(String connectionString, String targetName, String stagingName, String[] pkeys, String[] updateFields)
//        {
//            // fetching sql
//            // select pkeys, update fields from t1 join t2 on pkey list where not ( update fields equal list)  order by pkey list
//            // The template is used twice: once to get all target table data. Next all staging table data.
//            String template = @"
//select {2}, {3} 
//  from {0}
//  join {1} on {4}
// where {5}
//order by {2}
//";
//            //String sql = String.Format(template, Helper.SafeTableName(targetName), Helper.SafeTableName(stagingName), 
//            //                MakePKListSqlPart(pkeys, targetName),
//            //                MakeUpdateListSqlPart(updateFields, targetName),
//            //                MakeJoinClauseSqlPart(pkeys, targetName, stagingName),
//            //                MakeInequalityTestSqlPart(updateFields, targetName, stagingName));
			
//            //DataTable target = Query(connectionString, new String[]{sql}, stagingName);
			
//            String sql =  String.Format(template, Helper.SafeTableName(stagingName), Helper.SafeTableName(targetName), 
//                            MakePKListSqlPart(pkeys, stagingName),
//                            MakeUpdateListSqlPart(updateFields, stagingName),
//                            MakeJoinClauseSqlPart(pkeys, stagingName, targetName),
//                            MakeInequalityTestSqlPart(updateFields, stagingName, targetName));
//            DataTable staging = Query(connectionString, new String[]{sql}, stagingName);
			
//            DataTable target = staging.Clone();
//            target.TableName = targetName;
//            foreach (DataRow dr in staging.Rows)
//            {
//                target.ImportRow(dr);
//            }

//            DbView.ViewDefinition viewDef = new DbView.ViewDefinition();
//            viewDef.ConfigureFromTable(target);
			
//            sql = "select * from " + target.TableName;
//            // cur from old code
//            int status = 0;

//            int modCount = 0;
//            foreach (DataRow dr in target.Rows)
//            {
//                dr.SetModified();
//                ++modCount;
//            }
//            String statusMessage = String.Format("{0} changed rows", modCount);
//            // do it.
//            SQLiteDataAdapter myDataAdapter = new SQLiteDataAdapter();
//            // the sql and the command builder automatically generate the 
//            // insert/update/delete sql
//            SQLiteConnection databaseConnection = new SQLiteConnection(connectionString);
//            databaseConnection.Open();
				
//            myDataAdapter.SelectCommand = new SQLiteCommand(sql, databaseConnection);
//            SQLiteCommandBuilder cb = new SQLiteCommandBuilder(myDataAdapter);

//            // Without the SqlCommandBuilder this line would fail
//            // below apparently checks the state of each row in the table
//            // and applies the correct sql to update it.
//            myDataAdapter.Update(target);
//            databaseConnection.Close();
//        }


//        private String MakeInequalityTestSqlPart(string[] updateFields, string targetName, string stagingName)
//        {
//            List<String> clauses = new List<string>();
//            foreach (String field in updateFields)
//                clauses.Add(String.Format("({0}.{2} != {1}.{2} or COALESCE({0}.{2}, {1}.{2}) is not NULL)", Helper.SafeTableName(targetName), Helper.SafeTableName(stagingName), Helper.SafeSqlField(field)));
//            return String.Join(" or ", clauses.ToArray());
//        }

//        private String MakeJoinClauseSqlPart(string[] pkeys, string targetName, string stagingName)
//        {
//            List<String> clauses = new List<string>();
//            foreach (String field in pkeys)
//                clauses.Add(String.Format("{0}.{2} = {1}.{2}", Helper.SafeTableName(targetName), Helper.SafeTableName(stagingName),  Helper.SafeSqlField(field)));
//            return String.Join(" and ", clauses.ToArray());
//        }

//        private String MakeUpdateListSqlPart(string[] updateFields, string targetName)
//        {
//            List<String> clauses = new List<string>();
//            foreach (String field in updateFields)
//                clauses.Add(String.Format("{0}.{1}", Helper.SafeTableName(targetName),  Helper.SafeSqlField(field)));
//            return String.Join(", ", clauses.ToArray());
//        }

//        private String MakePKListSqlPart(string[] pkeys, string targetName)
//        {
//            List<String> clauses = new List<string>();
//            foreach (String field in pkeys)
//                clauses.Add(String.Format("{0}.{1}", Helper.SafeTableName(targetName),  Helper.SafeSqlField(field)));
//            return String.Join(", ", clauses.ToArray());
//        }

		private SQLiteConnection HeldConnection {get; set; }
		private SQLiteDataReader DataReader {get; set; }

		public void ReadInit(String connectionString, String sql)
		{
			HeldConnection = new SQLiteConnection(connectionString);
			SQLiteCommand cmd  = new SQLiteCommand(sql, HeldConnection);
			// open the connection
			HeldConnection.Open();
			// 1. get an instance of the SqlDataReader
			DataReader = cmd.ExecuteReader();
		}
		
		// read the data until exhausted
		public bool ReadNext(out object[] data)
		{
			data = new object[DataReader.FieldCount];
			bool readData = DataReader.Read();
			if (readData)
			{
			    DataReader.GetValues(data);
			}
			return readData;
		}
		// finish
		public void ReadClose()
		{
			DataReader.Close();
			HeldConnection.Close();
		}		
	}


	public class SqliteImportExportInterfaceImplementation : DbViewDriver.IDatabaseExportSource
	{
		public SqliteImportExportInterfaceImplementation()
		{
			this.dataAccessor = new SqliteDataAccessor();
		}
		/////////////////////////////////////////////////////////////////////
		//  DbViewDriver.IDatabaseExportSource interface
		public String ConnectionString
		{
		    get {return this.connectionString;}
		    set {connectionString = value;}
		}

		public DbView.FieldInfo[] GetSchema(String tableName)
		{
			if (Fields == null)
			{
				SqlTableSchema schema = Helper.MakeSchemaFor(tableName,  ConnectionString);
				Fields = new List<DbView.FieldInfo>();
				for (int idx = 0; idx < schema.Count; ++idx)
					Fields.Add(schema[idx]);
			}
			return Fields.ToArray();
		}

		public DataTable ReadData(String tableName, String [] fields, String filterClause, String orderClause)
		{
			String fieldList = "["+String.Join("],[", fields)+"]";
			String sql = String.Format("SELECT {1} from {0}", Helper.SafeTableName(tableName), fieldList);
			if (filterClause.Length > 0)
				sql += " where "+filterClause;
			if (orderClause.Length > 0)
				sql += " order by "+orderClause;
			DataTable dt = this.dataAccessor.GenericQuery(ConnectionString, sql, tableName);
			return dt;
		}

		private String MakeSql(String tableName, String [] fields, String filterClause, String orderClause, bool forCount)
		{
			String fieldList = "["+String.Join("],[", fields)+"]";
			if (forCount) fieldList = "count(*)"; // counting not fetching fields
			String sql = String.Format("SELECT {1} from {0}", Helper.SafeTableName(tableName), fieldList);
			if (filterClause.Length > 0)
				sql += " where "+filterClause;
			if (orderClause.Length > 0 && !forCount)   // don't sort if counting. Query optimizer probably dumps this anyway
				sql += " order by "+orderClause;
			return sql;
		}

		public int ReadInit(String tableName, String [] fields, String filterClause, String orderClause)
		{
			String sql = MakeSql(tableName, fields, filterClause, orderClause, true);
			DataTable dt = this.dataAccessor.Query(ConnectionString, new String[] {sql}, tableName);
			int rowCount = Convert.ToInt32(dt.Rows[0][0]);
			// now set up the reader
			sql = MakeSql(tableName, fields, filterClause, orderClause, false);
			this.dataAccessor.ReadInit(ConnectionString, sql);
			return rowCount;
		}
		
		// read the data until exhausted
		public bool ReadNext(out object[] data)
		{
			return this.dataAccessor.ReadNext(out data);
		}
		// finish
		public void ReadClose()
		{
			this.dataAccessor.ReadClose();
		}

		public DataTable CreateTransferTable(String tableName)
		{
			DbView.FieldInfo[] fields = GetSchema(tableName);
			String fieldList="";
			foreach (DbView.FieldInfo field in fields)
			{
				if (fieldList.Length > 0) fieldList += ",";
				fieldList += field.SafeName;
			}
			String sql = String.Format("SELECT {1} from {0} limit 0", Helper.SafeTableName(tableName), fieldList);
			DataTable dt = this.dataAccessor.GenericQuery(ConnectionString, sql, tableName);
			return dt;
		}
		
		// This will throw exceptions. Its up to the caller to deal with them as there
		// is not much you can do to recover here.
		public void PopulateTable(String tableName, DataTable data, bool isStaging)
		{
			String targetTableName = (isStaging) ? MakeStagingTableName(tableName) : tableName;
			DbView.FieldInfo[] fields = GetSchema(tableName);
			String autoIncField = "";
			foreach (DbView.FieldInfo fi in fields)
				if (fi.IsAuto) autoIncField = fi.Name;
			// do the SQL CE bit
			// need to establish whether there is an identitiy column
			// check existance of tgt
			this.dataAccessor.PopulateTable(ConnectionString, targetTableName, data, autoIncField, isStaging);
		}

		public void CreateStagingTable(String tableName, bool isStaging, bool create)
		{
			if (!isStaging) return; // not necessary
			if (create)
			{
				CreateStagingTable(tableName);
			}
			else
			{
				String stagingTableName = MakeStagingTableName(tableName);
				String tableScript = "drop table "+ stagingTableName; // should work.
				// execute it
				bool success = false;
				this.dataAccessor.GenericExecute(ConnectionString, tableScript, out success);
			}
		}

		private String CreateStagingTable(String targetTableName)
		{
			// refactor into a new routine
			String stagingTableName = MakeStagingTableName(targetTableName);
			SqlTableSchema schema = Helper.MakeSchemaFor(targetTableName, ConnectionString);
			SqlServerScripter scripter = new SqlServerScripter(targetTableName, schema);
			String makeTableScript = scripter.ScriptCreateTable(true);
			makeTableScript = ReplaceTargetWithStaging(makeTableScript, targetTableName, stagingTableName);
			// TODO: need to find whether it exists and make it if not.
			//makeTableScript = "if object_id('"+stagingTableName+"') is null "+ makeTableScript;
			// execute it
			bool success = false;
			this.dataAccessor.GenericExecute(ConnectionString, makeTableScript, out success);
			return stagingTableName;
		}

		private String ReplaceTargetWithStaging(String script, String targetTableName, String stagingTableName)
		{
			// Deal with schema tweak in the script
			String seek = Helper.SafeTableName(targetTableName);
			String repl = Helper.SafeTableName(stagingTableName);

			script = script.Replace(seek, repl);
			// now attend the PK_FIELD
			// remember to deal with schema stuff
			seek = seek.Insert(1, "PK_");
			repl = repl.Insert(1, "PK_");
			script = script.Replace(seek, repl);
			return script;
		}

		// If staging update the from the staging table according to the given mode
		public void UpdateFromStaging(String tableName, String [] importedFields, DbViewDriver.CsvImportMode mode)
		{
			String stagingTableName = MakeStagingTableName(tableName);
			// make source field list
			DbView.FieldInfo [] sourceFields = MakeSourceFieldList(importedFields);

			DbView.SqlScripter updateScripter = new DbView.SqlScripter(tableName, Fields.ToArray());
			bool success = false;
			string script = "";
			switch (mode)
			{
				case CsvImportMode.AppendOnly:
					script = updateScripter.ScriptMergeAppend(stagingTableName, sourceFields);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					// done
					break;
				case CsvImportMode.Replace:
					script = String.Format("delete from {0}", tableName);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					script = updateScripter.ScriptMergeAppend(stagingTableName, sourceFields);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					// done
					break;
				case CsvImportMode.UpdateOnly:
					script = updateScripter.ScriptMergeUpdateSqlite(stagingTableName, sourceFields);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					// done
					break;
				case CsvImportMode.Merge:
					script = updateScripter.ScriptMergeUpdateSqlite(stagingTableName, sourceFields);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					script = updateScripter.ScriptMergeAppend(stagingTableName, sourceFields);
					// execute
					this.dataAccessor.GenericExecute(ConnectionString, script, out success);
					// done
					break;
			}

			//if (schema.IdentityField != null && mode != DbViewDriver.CsvImportMode.UpdateOnly)
			//{
			//    this.dataAccessor.ResetAutoIdentity(this.connectionString, tableName);
			//}
		}


		public int CountRows(String tableName, String filterClause)
		{
			String sql = "Select count(*) from " + Helper.SafeTableName(tableName);
			if (filterClause.Length > 0) sql += " where "+filterClause;
			DataTable dt = this.dataAccessor.GenericQuery(ConnectionString, sql, "result");
			int rowCount = System.Convert.ToInt32(dt.Rows[0][0]);
			return rowCount;
		}

		// generate a field info list
		// NEW: keep
		private DbView.FieldInfo[] MakeSourceFieldList(string[] importedFields)
		{
			List<DbView.FieldInfo> sourceFields = new List<DbView.FieldInfo>();
			foreach (String field in importedFields)
			{
				foreach (DbView.FieldInfo fi in this.Fields)
				{
					if (fi.Name == field)
					{
						sourceFields.Add(fi);
						break;
					}
				}
			}
			return sourceFields.ToArray();
		}

		// This to find fields that will cause problems if imported directly from staging
		// Inherited from SQL Server. Best to remove if possible. 
		private DbView.FieldInfo [] MakeExcludedFieldList(String connectionString, String tableName, String[] importedFields, DbView.FieldInfo [] schema, DbViewDriver.CsvImportMode mode)
		{
			List<DbView.FieldInfo> fieldList = new List<DbView.FieldInfo>();
			// include Non-null fields with and without defaults. Actually we need to 
			foreach (String field in importedFields)
			{
				foreach (DbView.FieldInfo fi in schema)
				{
					if (!fi.IsPKey)
						if (fi.Name == field && !fi.AllowsNulls)
						{
							// a. find whether there are any blank values in staging
							int nullCount = CountNullsInStaging(connectionString, tableName, field);
							// if N then do not add
							// if Y and there is a default then add to special
							// if Y and there is no default then do not add and let the operation abort.
							// if Y and mode is append only abort
							if (nullCount > 0)
							{
								if (mode == DbViewDriver.CsvImportMode.AppendOnly)
									throw new Exception(String.Format("Unable to continue in append only mode because import data for field [{0}] contains empty values which [{0}] cannot have.", fi.Name));
							}
						}
				}
			}
			return fieldList.ToArray();
		}

		private int CountNullsInStaging(string connectionString, string tableName, string field)
		{
			String sql = String.Format("select count({1}) as C from {0}", Helper.SafeTableName(MakeStagingTableName(tableName)), field);
			DataTable dt = this.dataAccessor.GenericQuery(connectionString, sql, "result");
			return (int)dt.Rows[0][0];
		}
		private string MakeStagingTableName(string tableName)
		{
			// Ultimate goal is to refactor to use a real temp table
			// ## is a persistent temp table.
			// also remove schemaness
			// problems with bulk copy and global temp table. Use decorated name for now
			// return String.Format("##{0}", tableName.Replace(".", "_"));
			// Check for table length?
			return String.Format("{0}_stage", tableName.Replace(".", "_"));
		}

		SqliteDataAccessor dataAccessor;
		String connectionString;
		private List<DbView.FieldInfo> Fields {get; set;}
	}

}
